import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import matplotlib.ticker as ticker
import matplotlib.dates as mdates
def add_bar_labels(ax, formatter, spacing = 3 ):
for rect in ax.patches:
y_value = rect.get_height()
x_value = rect.get_x() + rect.get_width() / 2
space = spacing
va = 'bottom'
if y_value <0:
space *= -1
va = 'top'
label = formatter.format(y_value)
ax.annotate(label,
(x_value, y_value),
xytext=(0, space),
textcoords = "offset points",
ha="center",
va=va,
fontsize = 10,
color = "tab:gray")
factornames = ["Excess\nMarket", "Size", "Value-B2M", "Value-E2P", "Profitability", "Investment", "Momentum", "Reversal",
"Beta", "Volume", "Volatility"]
factorcodes = ["mkt", 'size', 'bm', 'ep','roe', 'ag', 'm12', 'm1','beta','idvc', 'dtvm']
path = r'.\sector-rotation-data'
def read_factor(code, filename = r".\input\factor\ten_factor_vw_{}_5.csv"):
data = pd.read_csv(filename.format(code), index_col=0)
data.index = pd.to_datetime(data.index, format="%Y-%m")
data["mkt"] = data["market"] - data["rf"]
return(data)
def factor_sum_plot(data, factorcodes, factornames, fig_title, fig_path, periods = 12, horizons = (1, 3, 1), **kwargs):
# ann_ret = lambda x: (x+1).prod()**(periods/len(x)) - 1
ann_ret = lambda x: np.mean(x)*periods
ann_vol = lambda x: x.std()*(periods**0.5)
sr = lambda x: ann_ret(x)/ann_vol(x)
# set grid layout
plt.style.use('seaborn')
fig = plt.figure(num= 1, figsize=(24,12)) #gridspec_kw = {'height_ratios':[1, 4]},
gs = gridspec.GridSpec(2,2, height_ratios = [1,1], width_ratios = [1, 1.4])
ax1 = fig.add_subplot(gs[0,0])
ax2 = fig.add_subplot(gs[1,0])
ax3 = fig.add_subplot(gs[0,1])
ax4 = fig.add_subplot(gs[1,1])
h1, h2, h3 = horizons
# upper left: annualized return in the past 12 months (52 weeks) and past 36 months (156 weeks)
pd.concat([data[factorcodes].iloc[-h1*periods:,:].apply(ann_ret).rename("last 12 months"),
data[factorcodes].iloc[-h2*periods:,:].apply(ann_ret).rename("last 36 months")]
, axis=1).T.plot.bar(ax = ax1, rot=1, width=0.8, colormap="Paired", fontsize = 12)
add_bar_labels(ax1, "{:.1%}")
ax1.legend(factornames, ncol=1, loc="center left", bbox_to_anchor = (1, 0.5), fontsize = 12)
xtick1 = [" - ".join([data.index[-h1*periods].strftime("%Y/%m"),data.index[-1].strftime("%Y/%m")])]
xtick1.append(" - ".join([data.index[-h2*periods].strftime("%Y/%m"),data.index[-1].strftime("%Y/%m")]))
ax1.set_xticklabels(xtick1)
ax1.set_title("Annualized Return", size = 16)
# bottom left: annualized sharpe ratio in the past 12 months (52 weeks) and past 36 months (156 weeks)
pd.concat([data[factorcodes].iloc[-h1*periods:,:].apply(sr).rename("last year"),
data[factorcodes].iloc[-h2*periods:,:].apply(sr).rename("last 3 years")]
, axis=1).T.plot.bar(ax = ax2, rot=1, width=0.8, colormap="Paired", fontsize = 12)
add_bar_labels(ax2, "{:.2f}")
ax2.legend(factornames, ncol=1, loc="center left", bbox_to_anchor = (1, 0.5), fontsize = 12)
xtick2 = [" - ".join([data.index[-h1*periods].strftime("%Y/%m"),data.index[-1].strftime("%Y/%m")])]
xtick2.append(" - ".join([data.index[-h2*periods].strftime("%Y/%m"),data.index[-1].strftime("%Y/%m")]))
ax2.set_xticklabels(xtick2)
ax2.set_title("Annualized Sharpe Ratio", size = 16)
# upper right: cumulative return in the past 12 months or 52 weeks
cum_ret = data[factorcodes].iloc[-h3*periods-1:,:]+1
cum_ret.iloc[0, :] = np.ones(len(factorcodes))
tick3 = " - ".join([cum_ret.index[-h3*periods].strftime("%Y/%m"),cum_ret.index[-1].strftime("%Y/%m")])
cum_ret.index = mdates.date2num(cum_ret.index)
cum_ret.cumprod().plot(ax = ax3, colormap = "Paired", fontsize = 14, grid=True, legend = None, **kwargs)
ax3.set_xlabel("")
ax3.set_xlim([data.index[-h3*periods]-pd.Timedelta(days=20),data.index[-1]+pd.Timedelta(days=20)])
ax3.xaxis.set_major_locator(mdates.MonthLocator(bymonth = (6,12) if h3 >=2 else (3,9,6,12)))
ax3.set_title("Cumulative Return {}".format(tick3), size = 16)
ax3.yaxis.set_ticks_position('right')
# ax3.legend(factornames, ncol=1, loc="upper left", fontsize = 12)
# for i in range(len(factorcodes)):
# height = (data[factorcodes].iloc[-5*periods:,:]+1).prod()[i]
# text = "{:.1%}".format(height)
# ax3.annotate(xy=[data.index[-1]+pd.Timedelta(days=30), height], xytext = (1,0),
# textcoords = 'offset points', text = text, color="tab:grey", fontsize = 12)
cum_ret_roll = (data[factorcodes]+1).rolling(window = periods).apply(np.prod) - 1
cum_ret_roll = cum_ret_roll.iloc[-h3*periods+1:,:]
tick4 = " - ".join([cum_ret_roll.index[0].strftime("%Y/%m"),cum_ret_roll.index[-1].strftime("%Y/%m")])
cum_ret_roll.index = mdates.date2num(cum_ret_roll.index)
cum_ret_roll.plot(ax = ax4, colormap = "Paired", fontsize = 14, grid=True, legend = None, **kwargs)
ax4.set_xlabel("")
ax4.set_xlim([data.index[-h3*periods+1]-pd.Timedelta(days=20),data.index[-1]+pd.Timedelta(days=20)])
ax4.xaxis.set_major_locator(mdates.MonthLocator(bymonth = (6,12) if h3 >=2 else (3,9,6,12)))
ax4.set_title("Rolling 52-week Return {}".format(tick4), size = 16)
ax4.yaxis.set_ticks_position('right')
fig.suptitle(fig_title, fontsize = 18)
fig.tight_layout(rect = [0, 0.03, 1, 0.98])
plt.savefig(fig_path)
plt.show()
code = "hk"
data = read_factor(code)
factor_sum_plot(data, factorcodes, factornames, "Factor Performance Summary (Hong Kong)", "factor_summary_{}.png".format(code))
code = "us"
data = read_factor(code)
factor_sum_plot(data, factorcodes, factornames, "Factor Performance Summary (U.S.)", "factor_summary_{}.png".format(code))
code = "cn"
data = read_factor(code)
display(data.describe())
factor_sum_plot(data, factorcodes, factornames, "Factor Performance Summary (Shanghai-Shenzhen.)", "factor_summary_{}.png".format(code))
| size | idvc | beta | bm | m12 | m1 | roe | ag | dtvm | ep | market | rf | mkt | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 266.000000 | 266.000000 | 266.000000 | 266.000000 | 266.000000 | 266.000000 | 266.000000 | 266.000000 | 266.000000 | 266.000000 | 266.000000 | 266.000000 | 266.000000 |
| mean | 0.012132 | 0.007767 | -0.001913 | 0.003581 | 0.002173 | 0.005642 | 0.006235 | 0.003983 | 0.013794 | 0.008853 | 0.008623 | 0.001881 | 0.006742 |
| std | 0.072249 | 0.059669 | 0.059206 | 0.057077 | 0.057326 | 0.055125 | 0.057137 | 0.040892 | 0.060475 | 0.058804 | 0.077392 | 0.000585 | 0.077433 |
| min | -0.356786 | -0.195979 | -0.157931 | -0.256189 | -0.287557 | -0.296507 | -0.170075 | -0.149548 | -0.305313 | -0.204325 | -0.266088 | 0.001241 | -0.269474 |
| 25% | -0.026329 | -0.025911 | -0.039261 | -0.026778 | -0.027483 | -0.021495 | -0.028258 | -0.019108 | -0.017210 | -0.025798 | -0.040618 | 0.001241 | -0.042147 |
| 50% | 0.012597 | 0.008026 | -0.004233 | 0.003777 | 0.001018 | 0.006661 | 0.003550 | 0.004835 | 0.011125 | 0.009787 | 0.010305 | 0.001856 | 0.009007 |
| 75% | 0.049828 | 0.039312 | 0.035139 | 0.033030 | 0.036188 | 0.034251 | 0.035183 | 0.025644 | 0.042430 | 0.042739 | 0.043826 | 0.002263 | 0.041681 |
| max | 0.357761 | 0.208310 | 0.206149 | 0.322304 | 0.177692 | 0.195060 | 0.236803 | 0.141606 | 0.328830 | 0.285424 | 0.304778 | 0.003386 | 0.302482 |
data = read_portfolio(4)
factor_sum_plot(data, codes, names, "Sector Allocation Performance ($\gamma = 4$)", "sector_summary_4.png")
--------------------------------------------------------------------------- NameError Traceback (most recent call last) C:\Users\ARIZON~1\AppData\Local\Temp/ipykernel_16176/1601687472.py in <module> ----> 1 data = read_portfolio(4) 2 factor_sum_plot(data, codes, names, "Sector Allocation Performance ($\gamma = 4$)", "sector_summary_4.png") NameError: name 'read_portfolio' is not defined
data = read_portfolio(8)
factor_sum_plot(data, codes, names, "Sector Allocation Performance ($\gamma = 8$)", r".\plots\sector_summary_8.png")
data = read_factor_weekly("cn800", format = "%Y-%m-%d")
factor_sum_plot(data, factorcodes, factornames, "Monthly Factor Performance Summary (Shanghai-Shenzhen 800)",r".\plots\factor_cn800_5.png", periods = 52)
data = read_factor_weekly("hk300", format = "%Y-%m-%d")
factor_sum_plot(data, factorcodes, factornames, "Monthly Factor Performance Summary (Hong Kong Top 300)", r".\plots\factor_hk300.png", periods = 52)
data = read_factor_weekly("us1500", format = "%Y-%m-%d")
factor_sum_plot(data, factorcodes, factornames, "Monthly Factor Performance Summary (U.S. Top 1500)", r".\plots\factor_us1500_5.png", periods = 52)
factor_sum_plot(long, factorcode1, factorname1, "Monthly Factor Performance Summary (Hong Kong 300 - Long)",
r".\plots\factor_{}_long_5".format(code), periods = 52)
factor_sum_plot(short, factorcode1, factorname1, "Monthly Factor Performance Summary (Hong Kong 300 - Short)",
r".\plots\factor_{}_short_5".format(code), periods = 52)
factor_sum_plot(long, factorcode1, factorname1, "Monthly Factor Performance Summary (Shanghai-Shenzhen 800 - Long)",r".\plots\factor_{}_long_5".format(code), periods = 52)
factor_sum_plot(short, factorcode1, factorname1, "Monthly Factor Performance Summary (Shanghai-Shenzhen 800 - Short)",r".\plots\factor_{}_short_5".format(code), periods = 52)
factor_sum_plot(long, factorcode1, factorname1, "Monthly Factor Performance Summary (U.S. 1500 - Long)",r".\plots\factor_{}_long_5".format(code), periods = 52)
factor_sum_plot(short, factorcode1, factorname1, "Monthly Factor Performance Summary (U.S. 1500 - Short)",r".\plots\factor_{}_short_5".format(code), periods = 52)
factor_sum_plot(data, data.columns, data.columns, "Sector Rotation Performance (Shanghai-Shenzhen)",
r".\plots\performance_{}_5.png".format(code), periods = 52, horizons = (1, 5, 5))
factor_sum_plot(df_all, df_all.columns, df_all.columns, "Sector Rotation Performance (Hong Kong)",
r".\plots\performance_{}_5.png".format(code), periods = 52, horizons = (1, 5, 5))
factor_sum_plot(df_all, df_all.columns, df_all.columns, "Sector Rotation Performance (Hong Kong)",
r".\plots\performance_{}_5.png".format(code), periods = 52, horizons = (1, 5, 5))
data = pd.read_csv(
r".\sector-rotation-data\hsci_sectors_monthly.csv", index_col=0)
data.index = pd.to_datetime(data.index)
factor_sum_plot(data, data.columns, data.columns,
"Sector Performance (Hong Kong)", "hk_sum.png")